<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<style>
			html,
			body {
				overflow: hidden;
				background: black;
				padding: 0;
				margin: 0;
			}
		</style>
	</head>
	<body></body>
	<script>
		'use strict'

		const { PI, cos, sin, tan, abs, sqrt, pow, min, max, ceil, floor, round, random, atan2 } = Math
		const HALF_PI = 0.5 * PI
		const QUART_PI = 0.25 * PI
		const TAU = 2 * PI
		const TO_RAD = PI / 180
		const G = 6.67 * pow(10, -11)
		const EPSILON = 2.220446049250313e-16
		const rand = (n) => n * random()
		const randIn = (_min, _max) => rand(_max - _min) + _min
		const randRange = (n) => n - rand(2 * n)
		const fadeIn = (t, m) => t / m
		const fadeOut = (t, m) => (m - t) / m
		const fadeInOut = (t, m) => {
			let hm = 0.5 * m
			return abs(((t + hm) % m) - hm) / hm
		}
		const dist = (x1, y1, x2, y2) => sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2))
		const angle = (x1, y1, x2, y2) => atan2(y2 - y1, x2 - x1)
		const lerp = (a, b, amt) => (1 - amt) * a + amt * b
		const vh = (p) => p * window.innerHeight * 0.01
		const vw = (p) => p * window.innerWidth * 0.01
		const vmin = (p) => min(vh(p), vw(p))
		const vmax = (p) => max(vh(p), vw(p))
		const clamp = (n, _min, _max) => min(max(n, _min), _max)
		const norm = (n, _min, _max) => (n - _min) / (_max - _min)

		Array.prototype.lerp = function (t = [], a = 0) {
			this.forEach((n, i) => (this[i] = lerp(n, t[i], a)))
		}

		Float32Array.prototype.get = function (i = 0, n = 0) {
			const t = i + n

			let r = []

			for (; i < t; i++) {
				r.push(this[i])
			}

			return r
		}

		class PropsArray {
			constructor(count = 0, props = []) {
				this.count = count
				this.props = props
				this.values = new Float32Array(count * props.length)
			}
			get length() {
				return this.values.length
			}
			set(a = [], i = 0) {
				this.values.set(a, i)
			}
			setMap(o = {}, i = 0) {
				this.set(Object.values(o), i)
			}
			get(i = 0) {
				return this.values.get(i, this.props.length)
			}
			getMap(i = 0) {
				return this.get(i).reduce(
					(r, v, i) => ({
						...r,
						...{ [this.props[i]]: v },
					}),
					{}
				)
			}
			forEach(cb) {
				let i = 0

				for (; i < this.length; i += this.props.length) {
					cb(this.get.call(this, i), i, this)
				}
			}
			map(cb) {
				let i = 0

				for (; i < this.length; i += this.props.length) {
					this.set(cb(this.get.call(this, i), i, this), i)
				}
			}
		}
		!(function () {
			'use strict'
			var r = 0.5 * (Math.sqrt(3) - 1),
				e = (3 - Math.sqrt(3)) / 6,
				t = 1 / 6,
				a = (Math.sqrt(5) - 1) / 4,
				o = (5 - Math.sqrt(5)) / 20
			function i(r) {
				var e
				;(e =
					'function' == typeof r
						? r
						: r
						? (function () {
								var r = 0,
									e = 0,
									t = 0,
									a = 1,
									o =
										((i = 4022871197),
										function (r) {
											r = r.toString()
											for (var e = 0; e < r.length; e++) {
												var t = 0.02519603282416938 * (i += r.charCodeAt(e))
												;(t -= i = t >>> 0), (i = (t *= i) >>> 0), (i += 4294967296 * (t -= i))
											}
											return 2.3283064365386963e-10 * (i >>> 0)
										})
								var i
								;(r = o(' ')), (e = o(' ')), (t = o(' '))
								for (var n = 0; n < arguments.length; n++)
									(r -= o(arguments[n])) < 0 && (r += 1),
										(e -= o(arguments[n])) < 0 && (e += 1),
										(t -= o(arguments[n])) < 0 && (t += 1)
								return (
									(o = null),
									function () {
										var o = 2091639 * r + 2.3283064365386963e-10 * a
										return (r = e), (e = t), (t = o - (a = 0 | o))
									}
								)
						  })(r)
						: Math.random),
					(this.p = n(e)),
					(this.perm = new Uint8Array(512)),
					(this.permMod12 = new Uint8Array(512))
				for (var t = 0; t < 512; t++) (this.perm[t] = this.p[255 & t]), (this.permMod12[t] = this.perm[t] % 12)
			}
			function n(r) {
				var e,
					t = new Uint8Array(256)
				for (e = 0; e < 256; e++) t[e] = e
				for (e = 0; e < 255; e++) {
					var a = e + ~~(r() * (256 - e)),
						o = t[e]
					;(t[e] = t[a]), (t[a] = o)
				}
				return t
			}
			;(i.prototype = {
				grad3: new Float32Array([
					1,
					1,
					0,
					-1,
					1,
					0,
					1,
					-1,
					0,
					-1,
					-1,
					0,
					1,
					0,
					1,
					-1,
					0,
					1,
					1,
					0,
					-1,
					-1,
					0,
					-1,
					0,
					1,
					1,
					0,
					-1,
					1,
					0,
					1,
					-1,
					0,
					-1,
					-1,
				]),
				grad4: new Float32Array([
					0,
					1,
					1,
					1,
					0,
					1,
					1,
					-1,
					0,
					1,
					-1,
					1,
					0,
					1,
					-1,
					-1,
					0,
					-1,
					1,
					1,
					0,
					-1,
					1,
					-1,
					0,
					-1,
					-1,
					1,
					0,
					-1,
					-1,
					-1,
					1,
					0,
					1,
					1,
					1,
					0,
					1,
					-1,
					1,
					0,
					-1,
					1,
					1,
					0,
					-1,
					-1,
					-1,
					0,
					1,
					1,
					-1,
					0,
					1,
					-1,
					-1,
					0,
					-1,
					1,
					-1,
					0,
					-1,
					-1,
					1,
					1,
					0,
					1,
					1,
					1,
					0,
					-1,
					1,
					-1,
					0,
					1,
					1,
					-1,
					0,
					-1,
					-1,
					1,
					0,
					1,
					-1,
					1,
					0,
					-1,
					-1,
					-1,
					0,
					1,
					-1,
					-1,
					0,
					-1,
					1,
					1,
					1,
					0,
					1,
					1,
					-1,
					0,
					1,
					-1,
					1,
					0,
					1,
					-1,
					-1,
					0,
					-1,
					1,
					1,
					0,
					-1,
					1,
					-1,
					0,
					-1,
					-1,
					1,
					0,
					-1,
					-1,
					-1,
					0,
				]),
				noise2D: function (t, a) {
					var o,
						i,
						n = this.permMod12,
						f = this.perm,
						s = this.grad3,
						v = 0,
						h = 0,
						l = 0,
						u = (t + a) * r,
						d = Math.floor(t + u),
						p = Math.floor(a + u),
						M = (d + p) * e,
						m = t - (d - M),
						c = a - (p - M)
					m > c ? ((o = 1), (i = 0)) : ((o = 0), (i = 1))
					var y = m - o + e,
						w = c - i + e,
						g = m - 1 + 2 * e,
						A = c - 1 + 2 * e,
						x = 255 & d,
						q = 255 & p,
						D = 0.5 - m * m - c * c
					if (D >= 0) {
						var S = 3 * n[x + f[q]]
						v = (D *= D) * D * (s[S] * m + s[S + 1] * c)
					}
					var U = 0.5 - y * y - w * w
					if (U >= 0) {
						var b = 3 * n[x + o + f[q + i]]
						h = (U *= U) * U * (s[b] * y + s[b + 1] * w)
					}
					var F = 0.5 - g * g - A * A
					if (F >= 0) {
						var N = 3 * n[x + 1 + f[q + 1]]
						l = (F *= F) * F * (s[N] * g + s[N + 1] * A)
					}
					return 70 * (v + h + l)
				},
				noise3D: function (r, e, a) {
					var o,
						i,
						n,
						f,
						s,
						v,
						h,
						l,
						u,
						d,
						p = this.permMod12,
						M = this.perm,
						m = this.grad3,
						c = (r + e + a) * (1 / 3),
						y = Math.floor(r + c),
						w = Math.floor(e + c),
						g = Math.floor(a + c),
						A = (y + w + g) * t,
						x = r - (y - A),
						q = e - (w - A),
						D = a - (g - A)
					x >= q
						? q >= D
							? ((s = 1), (v = 0), (h = 0), (l = 1), (u = 1), (d = 0))
							: x >= D
							? ((s = 1), (v = 0), (h = 0), (l = 1), (u = 0), (d = 1))
							: ((s = 0), (v = 0), (h = 1), (l = 1), (u = 0), (d = 1))
						: q < D
						? ((s = 0), (v = 0), (h = 1), (l = 0), (u = 1), (d = 1))
						: x < D
						? ((s = 0), (v = 1), (h = 0), (l = 0), (u = 1), (d = 1))
						: ((s = 0), (v = 1), (h = 0), (l = 1), (u = 1), (d = 0))
					var S = x - s + t,
						U = q - v + t,
						b = D - h + t,
						F = x - l + 2 * t,
						N = q - u + 2 * t,
						C = D - d + 2 * t,
						P = x - 1 + 0.5,
						T = q - 1 + 0.5,
						_ = D - 1 + 0.5,
						j = 255 & y,
						k = 255 & w,
						z = 255 & g,
						B = 0.6 - x * x - q * q - D * D
					if (B < 0) o = 0
					else {
						var E = 3 * p[j + M[k + M[z]]]
						o = (B *= B) * B * (m[E] * x + m[E + 1] * q + m[E + 2] * D)
					}
					var G = 0.6 - S * S - U * U - b * b
					if (G < 0) i = 0
					else {
						var H = 3 * p[j + s + M[k + v + M[z + h]]]
						i = (G *= G) * G * (m[H] * S + m[H + 1] * U + m[H + 2] * b)
					}
					var I = 0.6 - F * F - N * N - C * C
					if (I < 0) n = 0
					else {
						var J = 3 * p[j + l + M[k + u + M[z + d]]]
						n = (I *= I) * I * (m[J] * F + m[J + 1] * N + m[J + 2] * C)
					}
					var K = 0.6 - P * P - T * T - _ * _
					if (K < 0) f = 0
					else {
						var L = 3 * p[j + 1 + M[k + 1 + M[z + 1]]]
						f = (K *= K) * K * (m[L] * P + m[L + 1] * T + m[L + 2] * _)
					}
					return 32 * (o + i + n + f)
				},
				noise4D: function (r, e, t, i) {
					var n,
						f,
						s,
						v,
						h,
						l,
						u,
						d,
						p,
						M,
						m,
						c,
						y,
						w,
						g,
						A,
						x,
						q = this.perm,
						D = this.grad4,
						S = (r + e + t + i) * a,
						U = Math.floor(r + S),
						b = Math.floor(e + S),
						F = Math.floor(t + S),
						N = Math.floor(i + S),
						C = (U + b + F + N) * o,
						P = r - (U - C),
						T = e - (b - C),
						_ = t - (F - C),
						j = i - (N - C),
						k = 0,
						z = 0,
						B = 0,
						E = 0
					P > T ? k++ : z++,
						P > _ ? k++ : B++,
						P > j ? k++ : E++,
						T > _ ? z++ : B++,
						T > j ? z++ : E++,
						_ > j ? B++ : E++
					var G = P - (l = k >= 3 ? 1 : 0) + o,
						H = T - (u = z >= 3 ? 1 : 0) + o,
						I = _ - (d = B >= 3 ? 1 : 0) + o,
						J = j - (p = E >= 3 ? 1 : 0) + o,
						K = P - (M = k >= 2 ? 1 : 0) + 2 * o,
						L = T - (m = z >= 2 ? 1 : 0) + 2 * o,
						O = _ - (c = B >= 2 ? 1 : 0) + 2 * o,
						Q = j - (y = E >= 2 ? 1 : 0) + 2 * o,
						R = P - (w = k >= 1 ? 1 : 0) + 3 * o,
						V = T - (g = z >= 1 ? 1 : 0) + 3 * o,
						W = _ - (A = B >= 1 ? 1 : 0) + 3 * o,
						X = j - (x = E >= 1 ? 1 : 0) + 3 * o,
						Y = P - 1 + 4 * o,
						Z = T - 1 + 4 * o,
						$ = _ - 1 + 4 * o,
						rr = j - 1 + 4 * o,
						er = 255 & U,
						tr = 255 & b,
						ar = 255 & F,
						or = 255 & N,
						ir = 0.6 - P * P - T * T - _ * _ - j * j
					if (ir < 0) n = 0
					else {
						var nr = (q[er + q[tr + q[ar + q[or]]]] % 32) * 4
						n = (ir *= ir) * ir * (D[nr] * P + D[nr + 1] * T + D[nr + 2] * _ + D[nr + 3] * j)
					}
					var fr = 0.6 - G * G - H * H - I * I - J * J
					if (fr < 0) f = 0
					else {
						var sr = (q[er + l + q[tr + u + q[ar + d + q[or + p]]]] % 32) * 4
						f = (fr *= fr) * fr * (D[sr] * G + D[sr + 1] * H + D[sr + 2] * I + D[sr + 3] * J)
					}
					var vr = 0.6 - K * K - L * L - O * O - Q * Q
					if (vr < 0) s = 0
					else {
						var hr = (q[er + M + q[tr + m + q[ar + c + q[or + y]]]] % 32) * 4
						s = (vr *= vr) * vr * (D[hr] * K + D[hr + 1] * L + D[hr + 2] * O + D[hr + 3] * Q)
					}
					var lr = 0.6 - R * R - V * V - W * W - X * X
					if (lr < 0) v = 0
					else {
						var ur = (q[er + w + q[tr + g + q[ar + A + q[or + x]]]] % 32) * 4
						v = (lr *= lr) * lr * (D[ur] * R + D[ur + 1] * V + D[ur + 2] * W + D[ur + 3] * X)
					}
					var dr = 0.6 - Y * Y - Z * Z - $ * $ - rr * rr
					if (dr < 0) h = 0
					else {
						var pr = (q[er + 1 + q[tr + 1 + q[ar + 1 + q[or + 1]]]] % 32) * 4
						h = (dr *= dr) * dr * (D[pr] * Y + D[pr + 1] * Z + D[pr + 2] * $ + D[pr + 3] * rr)
					}
					return 27 * (n + f + s + v + h)
				},
			}),
				(i._buildPermutationTable = n),
				'undefined' != typeof define &&
					define.amd &&
					define(function () {
						return i
					}),
				'undefined' != typeof exports
					? (exports.SimplexNoise = i)
					: 'undefined' != typeof window && (window.SimplexNoise = i),
				'undefined' != typeof module && (module.exports = i)
		})()
		;('use strict')

		const backgroundColor = 'rgba(0,0,10,0.5)'
		const baseHue = rand(360)
		const rangeHue = 180
		const tentacleCount = 30
		const segmentCountMin = 10
		const segmentCountMax = 20
		const segmentLengthMin = 20
		const segmentLengthMax = 40
		const colonyRadius = 200

		let canvas
		let ctx
		let center
		let mouse
		let tick
		let simplex
		let tentacle
		let tentacles

		class Tentacle {
			constructor(x, y, segmentNum, baseLength, baseDirection) {
				this.base = [x, y]
				this.position = [x, y]
				this.target = [x, y]
				this.segmentNum = segmentNum
				this.baseLength = baseLength
				this.baseDirection = baseDirection
				this.segmentProps = ['x1', 'y1', 'x2', 'y2', 'l', 'd', 'h']
				this.segments = new PropsArray(segmentNum, this.segmentProps)
				this.follow = false

				let i = this.segments.length - this.segmentProps.length
				let x1, y1, x2, y2, l, d, h

				l = this.baseLength
				d = this.baseDirection

				for (; i >= 0; i -= this.segmentProps.length) {
					x1 = x2 || this.position[0]
					y1 = y2 || this.position[1]
					x2 = x1 - l * cos(d)
					y2 = y1 - l * sin(d)
					d += 0.309
					l *= 0.98
					h = baseHue + fadeIn(i, this.segments.length) * rangeHue
					this.segments.set([x1, y1, x2, y2, l, d, h], i)
				}
			}
			setCtx(ctx) {
				this.ctx = ctx
			}
			setTarget(target) {
				this.target = target
			}
			updateBase() {
				let t = simplex.noise3D(this.base[0] * 0.005, this.base[1] * 0.005, tick * 0.005) * TAU

				this.base.lerp([this.base[0] + 20 * cos(t), this.base[1] + 20 * sin(t)], 0.025)
			}
			async update() {
				let target = this.position
				let i = this.segments.length - this.segmentProps.length
				let promises = []

				this.position.lerp(this.target, 0.015)
				!this.follow && this.updateBase()

				for (; i >= 0; i -= this.segmentProps.length) {
					promises.push(
						new Promise((resolve) => {
							let [x1, y1, x2, y2, l, d, h] = this.segments.get(i)
							let t, n, tn

							x1 = target[0]
							y1 = target[1]
							t = angle(x1, y1, x2, y2)
							n = simplex.noise3D(x1 * 0.005, y1 * 0.005, (i + tick) * 0.005)

							tn = t + n * PI * 0.0125
							x2 = x1 + l * cos(tn)
							y2 = y1 + l * sin(tn)
							d = t

							target = [x2, y2]

							this.segments.set([x1, y1, x2, y2, l, d], i)
							this.drawSegment(x1, y1, x2, y2, h, n, i)

							resolve()
						})
					)
				}

				await Promise.all(promises)
			}
			drawSegment(x1, y1, x2, y2, h, n, i) {
				const fn = fadeInOut(1 + n, 2)
				const fa = fadeInOut(i, this.segments.length)
				const a = 0.25 * (fn + fa)

				this.ctx.beginPath()
				this.ctx.strokeStyle = `hsla(${h}, 50%, 50%, ${a})`
				this.ctx.moveTo(x2, y2)
				this.ctx.lineTo(x1, y1)
				this.ctx.stroke()
				this.ctx.beginPath()

				this.ctx.closePath()
				this.ctx.strokeStyle = `hsla(${h}, 50%, 50%, ${a + 0.5})`
				this.ctx.arc(x1, y1, fn * 3, 0, TAU)
				this.ctx.stroke()
				this.ctx.closePath()
			}
		}

		function setup() {
			tick = 0
			simplex = new SimplexNoise()
			mouse = [0, 0]
			createCanvas()
			resize()
			tentacles = []

			let i, t

			for (i = 0; i < tentacleCount; i++) {
				t = (i / tentacleCount) * TAU
				tentacle = new Tentacle(
					center[0] + colonyRadius * cos(rand(TAU)),
					center[1] + colonyRadius * sin(rand(TAU)),
					round(randIn(segmentCountMin, segmentCountMax)),
					round(randIn(segmentLengthMin, segmentLengthMax)),
					t
				)

				tentacle.setCtx(ctx.a)
				tentacles.push(tentacle)
			}

			loop()
		}

		function createCanvas() {
			canvas = {
				a: document.createElement('canvas'),
				b: document.createElement('canvas'),
			}

			canvas.b.style = `
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
	`
			document.body.appendChild(canvas.b)
			ctx = {
				a: canvas.a.getContext('2d'),
				b: canvas.b.getContext('2d'),
			}

			center = [0.5 * canvas.a.width, 0.5 * canvas.a.height]
		}

		function resize() {
			const { innerWidth, innerHeight } = window

			canvas.a.width = innerWidth
			canvas.a.height = innerHeight

			ctx.a.drawImage(canvas.b, 0, 0)

			canvas.b.width = innerWidth
			canvas.b.height = innerHeight

			ctx.b.drawImage(canvas.a, 0, 0)

			center[0] = 0.5 * canvas.a.width
			center[1] = 0.5 * canvas.a.height
		}

		function renderBackground() {
			ctx.a.clearRect(0, 0, canvas.b.width, canvas.b.height)
			ctx.b.fillStyle = backgroundColor
			ctx.b.fillRect(0, 0, canvas.a.width, canvas.a.height)
		}

		function renderGlow() {
			ctx.b.save()
			ctx.b.filter = 'blur(8px) brightness(200%)'
			ctx.b.globalCompositeOperation = 'lighter'
			ctx.b.drawImage(canvas.a, 0, 0)
			ctx.b.restore()

			ctx.b.save()
			ctx.b.filter = 'blur(4px) brightness(200%)'
			ctx.b.globalCompositeOperation = 'lighter'
			ctx.b.drawImage(canvas.a, 0, 0)
			ctx.b.restore()
		}

		function renderToScreen() {
			ctx.b.save()
			ctx.b.globalCompositeOperation = 'lighter'
			ctx.b.drawImage(canvas.a, 0, 0)
			ctx.b.restore()
		}

		async function loop() {
			tick++

			renderBackground()

			await Promise.all(tentacles.map((tentacle) => tentacle.update()))

			renderGlow()
			renderToScreen()

			window.requestAnimationFrame(loop)
		}

		window.addEventListener('load', setup)
		window.addEventListener('resize', resize)
		window.addEventListener('mousemove', (e) => {
			tentacles.forEach((tentacle, i) => {
				const t = (i / tentacles.length) * TAU
				tentacle.setTarget([
					e.clientX + colonyRadius * cos(t + tick * 0.05),
					e.clientY + colonyRadius * sin(t + tick * 0.05),
				])
				tentacle.follow = true
			})
		})
		window.addEventListener('mouseout', () => {
			tentacles.forEach((tentacle) => {
				tentacle.base = [center[0] + colonyRadius * cos(rand(TAU)), center[1] + colonyRadius * sin(rand(TAU))]

				tentacle.setTarget(tentacle.base)
				tentacle.follow = false
			})
		})
	</script>
</html>
